import _ from "lodash";
import type { BaseLineInfo, BaseStyleInfo, ShadowDrawInfo } from "..";

export interface DrawerAopCfg {
  // 是否保持原来的 ctx 样式，默认 true
  keepCtx?: boolean;
  // 是否在开始处执行 ctx.beginPath()，默认 true
  beginPath?: boolean;
  // 是否在最后自动添加 fill 和 stroke 函数，默认 false
  dealStrokeFill?: boolean;
}

/**
 * 统一处理阴影配置信息
 * @param ctx
 * @param drawInfo
 * @returns
 */
function setShadow(ctx: CanvasRenderingContext2D, drawInfo: ShadowDrawInfo) {
  if (!drawInfo) return;
  if (drawInfo.shadowColor) {
    ctx.shadowColor = drawInfo.shadowColor!;
    ctx.shadowOffsetX = 5;
    ctx.shadowOffsetY = 5;
    ctx.shadowBlur = 5;
  }
  if (drawInfo.shadowBlur) ctx.shadowBlur = drawInfo.shadowBlur;
  if (!_.isNil(drawInfo.shadowOffsetX))
    ctx.shadowOffsetX = drawInfo.shadowOffsetX;
  if (!_.isNil(drawInfo.shadowOffsetY))
    ctx.shadowOffsetY = drawInfo.shadowOffsetY;
}

function setStyle(ctx: CanvasRenderingContext2D, drawInfo: BaseStyleInfo) {
  if (!drawInfo) return;
  if (drawInfo.fillStyle) ctx.fillStyle = drawInfo.fillStyle;
  if (drawInfo.strokeStyle) ctx.strokeStyle = drawInfo.strokeStyle;
}

function setLine(ctx: CanvasRenderingContext2D, drawInfo: BaseLineInfo) {
  if (!drawInfo) return;
  const { lineWidth, lineJoin, miterLimit, lineCap, lineDash } = drawInfo;
  if (lineWidth) ctx.lineWidth = lineWidth;
  if (lineJoin) ctx.lineJoin = lineJoin;
  if (!_.isNil(miterLimit)) ctx.miterLimit = miterLimit;
  if (lineCap) ctx.lineCap = lineCap;
  if (lineDash) {
    ctx.setLineDash(lineDash);
  }
}

/**
 * 统一处理需要的配置信息
 * @param cfg
 * @returns
 */
export function drawerAop(cfg: DrawerAopCfg = {}) {
  let { keepCtx, beginPath, dealStrokeFill } = cfg;
  if (_.isUndefined(keepCtx)) keepCtx = true;
  if (_.isUndefined(beginPath)) beginPath = true;
  if (_.isUndefined(dealStrokeFill)) dealStrokeFill = false;
  return function (
    _target: Object,
    _propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
      const ctx: CanvasRenderingContext2D = args[0];
      if (keepCtx) {
        ctx.save();
      }
      const drawInfo = args[1];
      setShadow(ctx, drawInfo);
      setStyle(ctx, drawInfo);
      setLine(ctx, drawInfo);
      if (beginPath) ctx.beginPath();
      const result = original.call(this, ...args);
      if (dealStrokeFill) {
        if (drawInfo.fillStyle) {
          ctx.fill();
        }
        if (drawInfo.strokeStyle) {
          ctx.stroke();
        }
        if (!drawInfo.fillStyle && !drawInfo.strokeStyle) {
          ctx.stroke();
        }
      }
      if (keepCtx) {
        ctx.restore();
      }
      return result;
    };
  };
}
